package com.jumi.microservice.payment;

import com.alibaba.fastjson.JSON;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.SendResult;
import com.aliyun.openservices.ons.api.bean.ProducerBean;
import com.jumi.microservice.common.base.dto.BaseRequest;
import com.jumi.microservice.common.base.dto.BaseResponse;
import com.jumi.microservice.common.enumeration.CommonResponseEnum;
import com.jumi.microservice.common.exception.BaseException;
import com.jumi.microservice.common.exception.BusinessException;
import com.jumi.microservice.common.utils.HttpClientUtil;
import com.jumi.microservice.config.WxH5PayConfig;
import com.jumi.microservice.constant.PayChannelEnum;
import com.jumi.microservice.context.WxPayContext;
import com.jumi.microservice.dto.*;
import com.jumi.microservice.exception.PayExceptionEnum;
import com.jumi.microservice.interfaces.BasePayment;
import com.jumi.microservice.interfaces.PayContext;
import com.jumi.microservice.rocketmq.RocketMqConfig;
import com.jumi.microservice.utils.ClientCustomSsl;
import com.jumi.microservice.utils.WxBuildRequestUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * @author scw
 * @Date 2020-11-04 09:30
 */
@Service
public class WxPayH5Payment extends BasePayment {

    private static final Logger log = LoggerFactory.getLogger(WxPayH5Payment.class);
    @Resource
    private WxH5PayConfig wxH5PayConfig;
    @Resource
    private ClientCustomSsl clientCustomSsl;
    @Resource
    ProducerBean producerBean;
    @Autowired
    RocketMqConfig rocketMqConfig;

    @Override
    public PayContext createContext(BaseRequest request) {
        PaymentRequest paymentRequest = (PaymentRequest) request;
        SortedMap<String, Object> parameters = new TreeMap<>();
        parameters.put("appid", wxH5PayConfig.getAppId());
        parameters.put("mch_id", wxH5PayConfig.getMchId());
        parameters.put("device_info", "WEB");
        parameters.put("nonce_str", WxBuildRequestUtil.getNonceStr());
        parameters.put("body", paymentRequest.getSubject());
        parameters.put("out_trade_no", paymentRequest.getPaymentSn());
        parameters.put("total_fee", paymentRequest.getTotalAmount().multiply(BigDecimal.valueOf(100)).intValue());
        //parameters.put("total_fee", new BigDecimal("0.01").multiply(BigDecimal.valueOf(100)).intValue());
        parameters.put("spbill_create_ip", paymentRequest.getClientIp());
        parameters.put("trade_type", "JSAPI");
        parameters.put("sign_type", "MD5");
        parameters.put("notify_url", wxH5PayConfig.getNotifyUrl());
        parameters.put("openid", paymentRequest.getOpenId());
        parameters.put("sign", WxBuildRequestUtil.createSign(parameters, wxH5PayConfig.getKey()));
        String xml = WxBuildRequestUtil.getRequestXml(parameters);
        WxPayContext context = new WxPayContext();
        context.setRequestXml(xml);
        return context;
    }

    @Override
    public void preProcess(PayContext context) {
    }

    @Override
    public BaseResponse coreProcess(PayContext context) {
        log.info("进入微信H5支付核心处理逻辑");
        WxPayContext ctx = (WxPayContext) context;
        String xml = HttpClientUtil.httpPost(wxH5PayConfig.getUnifiedOrderUrl(), ctx.getRequestXml());
        log.info("进入微信H5支付核心处理逻辑返回xml [{}]",xml);
        Map<String, String> res = WxBuildRequestUtil.doXMLParse(xml);
        log.info("进入微信H5支付核心处理逻辑返回 [{}]",JSON.toJSONString(res));
        if ("SUCCESS".equals(res.get("return_code"))) {
            if ("SUCCESS".equals(res.get("result_code"))) {
                WxPayResponse response = new WxPayResponse();
                SortedMap<String, Object> sortedMap = new TreeMap<>();
                sortedMap.put("appId", wxH5PayConfig.getAppId());
                sortedMap.put("timeStamp", System.currentTimeMillis() / 1000);
                sortedMap.put("nonceStr", WxBuildRequestUtil.getNonceStr());
                sortedMap.put("package", "prepay_id=" + res.get("prepay_id"));
                sortedMap.put("signType", "MD5");
                String sign = WxBuildRequestUtil.createSign(sortedMap, wxH5PayConfig.getKey());
                sortedMap.put("paySign", sign);
                response.setRequestMap(sortedMap);
                return response;
            } else {
                log.error(res.get("err_code: " + res.get("err_code_des")));
                throw new BaseException(500,res.get("err_code_des"));
            }
        } else {
            log.error(res.get("return_msg"));
            throw new BaseException(CommonResponseEnum.SERVER_ERROR.getCode(),CommonResponseEnum.SERVER_ERROR.getMessage());
        }
    }

    @Override
    public void afterProcess() {

    }

    @Override
    public String getPayChannel() {
        return PayChannelEnum.WX_PAY_H5.name();
    }

    @Override
    public BaseResponse completePayment(BaseRequest request) {
        PayNotifyResponse response = new PayNotifyResponse();
        PayNotifyRequest req = (PayNotifyRequest) request;
        String xml = req.getXml();
        Map<String, String> xmlMap = WxBuildRequestUtil.doXMLParse(xml);
        SortedMap<String, Object> paramMap = new TreeMap<>();
        xmlMap.forEach(paramMap::put);
        boolean checkSign = WxBuildRequestUtil.checkSign(paramMap, wxH5PayConfig.getKey());
        if (checkSign) {
            String resultCode = paramMap.get("result_code").toString();
            if ("SUCCESS".equals(resultCode)) {
                //TODO 校验完毕修改订单
                MqMessage mqMessage = new MqMessage();
                mqMessage.setPayNo(paramMap.get("out_trade_no").toString());
                mqMessage.setThirdPartyPaySn(paramMap.get("transaction_id").toString());
                log.info("微信H5支付发送消息:[{}]", JSON.toJSONString(mqMessage));
                Message message = new Message(rocketMqConfig.getTopic(), rocketMqConfig.getTag(), "", JSON.toJSONBytes(mqMessage));
                SendResult sendResult = producerBean.send(message);
                log.info("微信H5支付发送消息成功[{}],[{}]", sendResult.getMessageId(), sendResult.getTopic());
                response.setBody(WxBuildRequestUtil.setXML("SUCCESS", "OK"));
            } else {
                //TODO 订单支付失败
                throw new BaseException(PayExceptionEnum.WXPAY_NOTIFY_ERROR);
            }
        } else {
            throw new BaseException(PayExceptionEnum.CHECK_SIGN_ERROR);
        }
        return response;
    }

    @Override
    public PaymentResponse refund(BaseRequest request) {
        RefundRequest refundRequest = (RefundRequest) request;
        PaymentResponse response = new PaymentResponse();
        SortedMap<String, Object> parameters = new TreeMap<>();
        parameters.put("appid", wxH5PayConfig.getAppId());
        parameters.put("mch_id", wxH5PayConfig.getMchId());
        parameters.put("nonce_str", WxBuildRequestUtil.getNonceStr());
        parameters.put("transaction_id", refundRequest.getThirdPartyPaySn());
        parameters.put("out_trade_no", refundRequest.getPayNo());
        parameters.put("out_refund_no", refundRequest.getOrderSn());
        parameters.put("total_fee", refundRequest.getPayTotalAmount().multiply(BigDecimal.valueOf(100)).intValue());
        parameters.put("refund_fee", refundRequest.getAmount().multiply(BigDecimal.valueOf(100)).intValue());
        parameters.put("sign", WxBuildRequestUtil.createSign(parameters, wxH5PayConfig.getKey()));
        String requestXml = WxBuildRequestUtil.getRequestXml(parameters);
        String resultXml = clientCustomSsl.doPost(wxH5PayConfig.getRefundUrl(), requestXml);
        Map<String, String> paramMap = WxBuildRequestUtil.doXMLParse(resultXml);
        Map<String, String> returnMap = new HashMap<>();
        String params = "{" +
                "\"appid\":\"" + parameters.get("appid") +
                "\",\"mch_id\":\"" + parameters.get("mch_id") +
                "\",\"nonce_str\":\"" + parameters.get("nonce_str") +
                "\",\"transaction_id\":\"" + parameters.get("transaction_id") +
                "\",\"out_trade_no\":\"" + parameters.get("out_trade_no") +
                "\",\"out_refund_no\":\"" + parameters.get("out_refund_no") +
                "\",\"total_fee\":\"" + parameters.get("total_fee") +
                "\",\"refund_fee\":\"" + parameters.get("refund_fee") +
                "\",\"sign\":\"" + parameters.get("sign")
                + "\"}";
        returnMap.put("parameters", params);
        returnMap.put("return_code", paramMap.get("return_code"));
        returnMap.put("return_msg", paramMap.get("return_msg"));
        returnMap.put("result_code", paramMap.get("result_code"));
        if ("SUCCESS".equals(paramMap.get("result_code"))) {
            returnMap.put("appid", paramMap.get("appid"));
            returnMap.put("mch_id", paramMap.get("mch_id"));
            returnMap.put("nonce_str", paramMap.get("nonce_str"));
            returnMap.put("transaction_id", paramMap.get("transaction_id"));
            returnMap.put("out_trade_no", paramMap.get("out_trade_no"));
            returnMap.put("out_refund_no", paramMap.get("out_refund_no"));
            returnMap.put("refund_id", paramMap.get("refund_id"));
            returnMap.put("refund_fee", paramMap.get("refund_fee"));
            returnMap.put("total_fee", paramMap.get("total_fee"));
            returnMap.put("cash_fee", paramMap.get("sign"));
            returnMap.put("sign", paramMap.get("sign"));
            response.setResult(true);
        } else {
            returnMap.put("err_code", paramMap.get("err_code"));
            returnMap.put("err_code_des", paramMap.get("err_code_des"));
            response.setResult(false);
        }
        response.setResultInfo(returnMap.toString());
        return response;
    }
}
